package com.kanche.bigbaby.collection;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.media.ExifInterface;
import android.media.ThumbnailUtils;

import com.nostra13.universalimageloader.core.process.BitmapProcessor;
import com.squareup.picasso.Transformation;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

public class ImageUtils {
    public static final int SMALL_IMG_WIDTH = 640;
    public static final int SMALL_IMG_HEIGHT = 480;

    public static final int LARGE_IMG_WIDTH = 800;
    public static final int LARGE_IMG_HEIGHT = 600;

    public static final int IMG_QUALITY = 85;

    public static final long memoryThreshold = 1024L * 1024L * 1024L;
    public static final int OPTIONS_RECYCLE_INPUT = 0x2;
    private static final int OPTIONS_NONE = 0x0;
    private static final int OPTIONS_SCALE_UP = 0x1;

    public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
                .getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);
        final float roundPx = pixels;

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);
        return output;
    }

    // Copy file
    public static void copyFile(File sourceFile, File targetFile) throws IOException {
        BufferedInputStream inBuff = null;
        BufferedOutputStream outBuff = null;
        try {
            // 新建文件输入流并对它进行缓冲
            inBuff = new BufferedInputStream(new FileInputStream(sourceFile));

            // 新建文件输出流并对它进行缓冲
            outBuff = new BufferedOutputStream(new FileOutputStream(targetFile));

            // 缓冲数组
            byte[] b = new byte[1024 * 5];
            int len;
            while ((len = inBuff.read(b)) != -1) {
                outBuff.write(b, 0, len);
            }
            // 刷新此缓冲的输出流
            outBuff.flush();
        } finally {
            // 关闭流
            if (inBuff != null)
                inBuff.close();
            if (outBuff != null)
                outBuff.close();
        }
    }

    /**
     * Save as JPEG images
     *
     * @param bitmap
     * @param path
     */
    public static boolean saveImageAsJPGE(Bitmap bitmap, String path, int quality) {
        File file = new File(path);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(file);
            if (bitmap != null && bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) {
                out.flush();
                return true;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                    && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    public static Bitmap decodeSampledBitmapFromPath(String path,
                                                     int reqWidth, int reqHeight) {
        InputStream in = null;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        // First decode with inJustDecodeBounds=true to check dimensions
        options.inJustDecodeBounds = true;
        try {
            in = new FileInputStream(path);
            BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


        try {
            in = new FileInputStream(path);
            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeStream(in);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static boolean cropImage(String sourcePath, String targetPath, int imageWidth, int imageHeight, boolean deleteOriginal) {
        boolean result;
        try {
//            Bitmap bitmap = decodeSampledBitmapFromPath(sourcePath, imageWidth, imageHeight);
            Bitmap bitmap = PhotoMarkerUtil.dealPhotoFile(sourcePath);
            if (bitmap == null) {
                result = false;
            } else {
                ExInfo sourceExif = defineExifOrientation(sourcePath);
//                Bitmap scaled = Bitmap.createScaledBitmap(bitmap, imageWidth, imageHeight, true);

                Bitmap scaled = ThumbnailUtils.extractThumbnail(dealWithExif(bitmap, sourceExif), imageWidth, imageHeight);
//                bitmap.recycle();

                result = saveImageAsJPGE(scaled, targetPath, IMG_QUALITY);
                ExInfo targetExif = defineExifOrientation(targetPath);
//                scaled.recycle();
//                copyExif(sourcePath, targetPath);
                if (deleteOriginal)
                    new File(sourcePath).delete();
            }
        } catch (Exception e) {
            result = false;
        }
        return result;
    }

    public static Bitmap cropImage(String sourcePath, int imageWidth, int imageHeight, boolean deleteOriginal) {
        Bitmap result;
        try {
            Bitmap bitmap = decodeSampledBitmapFromPath(sourcePath, imageWidth, imageHeight);
            if (bitmap == null) {
                result = null;
            } else {
                ExInfo sourceExif = defineExifOrientation(sourcePath);

                result = ThumbnailUtils.extractThumbnail(dealWithExif(bitmap, sourceExif), imageWidth, imageHeight);

                if (deleteOriginal)
                    new File(sourcePath).delete();
            }
        } catch (Exception e) {
            result = null;
        }
        return result;
    }

    public static Bitmap dealWithExif(Bitmap source, ExInfo exif) {
        Bitmap finalBitmap;
        if (exif != null) {
            Matrix m = new Matrix();
            // Flip bitmap if need
            if (exif.isFlip()) {
                m.postScale(-1, 1);
            }
            // Rotate bitmap if need
            if (exif.getRotation() != 0) {
                m.postRotate(exif.getRotation());
            }

            finalBitmap = Bitmap.createBitmap(source, 0, 0, source.getWidth(), source
                    .getHeight(), m, true);
            if (finalBitmap != source) {
                source.recycle();
            }
        } else {
            finalBitmap = source;
        }
        return finalBitmap;
    }

    public static Bitmap extractThumbnail(
            Bitmap source, int width, int height, ExInfo exif) {
        if (source == null) {
            return null;
        }


        float scale;
        if (source.getWidth() < source.getHeight()) {
            scale = width / (float) source.getWidth();
        } else {
            scale = height / (float) source.getHeight();
        }
        Matrix matrix = new Matrix();

        matrix.setScale(scale, scale);

        Bitmap thumbnail = transform(matrix, source, width, height,
                OPTIONS_SCALE_UP | OPTIONS_NONE);
        return thumbnail;
    }

    /**
     * Transform source Bitmap to targeted width and height.
     */
    private static Bitmap transform(Matrix scaler,
                                    Bitmap source,
                                    int targetWidth,
                                    int targetHeight,
                                    int options) {
        boolean scaleUp = (options & OPTIONS_SCALE_UP) != 0;
        boolean recycle = (options & OPTIONS_RECYCLE_INPUT) != 0;

        int deltaX = source.getWidth() - targetWidth;
        int deltaY = source.getHeight() - targetHeight;
        if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
            /*
            * In this case the bitmap is smaller, at least in one dimension,
            * than the target.  Transform it by placing as much of the image
            * as possible into the target and leaving the top/bottom or
            * left/right (or both) black.
            */
            Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
                    Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b2);

            int deltaXHalf = Math.max(0, deltaX / 2);
            int deltaYHalf = Math.max(0, deltaY / 2);
            Rect src = new Rect(
                    deltaXHalf,
                    deltaYHalf,
                    deltaXHalf + Math.min(targetWidth, source.getWidth()),
                    deltaYHalf + Math.min(targetHeight, source.getHeight()));
            int dstX = (targetWidth - src.width()) / 2;
            int dstY = (targetHeight - src.height()) / 2;
            Rect dst = new Rect(
                    dstX,
                    dstY,
                    targetWidth - dstX,
                    targetHeight - dstY);
            c.drawBitmap(source, src, dst, null);
            if (recycle) {
                source.recycle();
            }
            c.setBitmap(null);
            return b2;
        }
        float bitmapWidthF = source.getWidth();
        float bitmapHeightF = source.getHeight();

        float bitmapAspect = bitmapWidthF / bitmapHeightF;
        float viewAspect = (float) targetWidth / targetHeight;

        if (bitmapAspect > viewAspect) {
            float scale = targetHeight / bitmapHeightF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        } else {
            float scale = targetWidth / bitmapWidthF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        }

        Bitmap b1;
        if (scaler != null) {
            // this is used for minithumb and crop, so we want to filter here.
            b1 = Bitmap.createBitmap(source, 0, 0,
                    source.getWidth(), source.getHeight(), scaler, true);
        } else {
            b1 = source;
        }

        if (recycle && b1 != source) {
            source.recycle();
        }

        int dx1 = Math.max(0, b1.getWidth() - targetWidth);
        int dy1 = Math.max(0, b1.getHeight() - targetHeight);

        Bitmap b2 = Bitmap.createBitmap(
                b1,
                dx1 / 2,
                dy1 / 2,
                targetWidth,
                targetHeight);

        if (b2 != b1) {
            if (recycle || b1 != source) {
                b1.recycle();
            }
        }

        return b2;
    }

    /**
     * Get a bitmap by a path,and you can specify the size
     *
     * @param path
     * @param size
     * @return
     */
    public static Bitmap createBitMapFromPath(String path, int size) {
        InputStream is = null;
        try {
            is = new FileInputStream(new File(path));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inSampleSize = size; // width and hight for one size of the original
        return BitmapFactory.decodeStream(is, null, options);
    }

    /**
     * Creates a centered bitmap of the desired size. Recycles the input.
     *
     * @param source
     */
    public static Bitmap extractMiniThumb(Bitmap source, int width, int height) {
        return extractMiniThumb(source, width, height, true);
    }

    public static Bitmap extractMiniThumb(Bitmap source, int width, int height,
                                          boolean recycle) {
        if (source == null) {
            return null;
        }

        float scale;
        if (source.getWidth() < source.getHeight()) {
            scale = width / (float) source.getWidth();
        } else {
            scale = height / (float) source.getHeight();
        }
        Matrix matrix = new Matrix();
        matrix.postScale(scale, scale);
        Bitmap miniThumbnail = transform(matrix, source, width, height, false);

        if (recycle && miniThumbnail != source) {
            source.recycle();
        }
        return miniThumbnail;
    }

    public static Bitmap transform(Matrix scaler, Bitmap source,
                                   int targetWidth, int targetHeight, boolean scaleUp) {
        int deltaX = source.getWidth() - targetWidth;
        int deltaY = source.getHeight() - targetHeight;
        if (!scaleUp && (deltaX < 0 || deltaY < 0)) {
            /*
             * In this case the bitmap is smaller, at least in one dimension,
             * than the target. Transform it by placing as much of the image as
             * possible into the target and leaving the top/bottom or left/right
             * (or both) black.
             */
            Bitmap b2 = Bitmap.createBitmap(targetWidth, targetHeight,
                    Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b2);

            int deltaXHalf = Math.max(0, deltaX / 2);
            int deltaYHalf = Math.max(0, deltaY / 2);
            Rect src = new Rect(deltaXHalf, deltaYHalf, deltaXHalf
                    + Math.min(targetWidth, source.getWidth()), deltaYHalf
                    + Math.min(targetHeight, source.getHeight()));
            int dstX = (targetWidth - src.width()) / 2;
            int dstY = (targetHeight - src.height()) / 2;
            Rect dst = new Rect(dstX, dstY, targetWidth - dstX, targetHeight
                    - dstY);
            c.drawBitmap(source, src, dst, null);
            return b2;
        }
        float bitmapWidthF = source.getWidth();
        float bitmapHeightF = source.getHeight();

        float bitmapAspect = bitmapWidthF / bitmapHeightF;
        float viewAspect = (float) targetWidth / targetHeight;

        if (bitmapAspect > viewAspect) {
            float scale = targetHeight / bitmapHeightF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        } else {
            float scale = targetWidth / bitmapWidthF;
            if (scale < .9F || scale > 1F) {
                scaler.setScale(scale, scale);
            } else {
                scaler = null;
            }
        }

        Bitmap b1;
        if (scaler != null) {
            // this is used for minithumb and crop, so we want to filter here.
            b1 = Bitmap.createBitmap(source, 0, 0, source.getWidth(),
                    source.getHeight(), scaler, true);
        } else {
            b1 = source;
        }

        int dx1 = Math.max(0, b1.getWidth() - targetWidth);
        int dy1 = Math.max(0, b1.getHeight() - targetHeight);

        Bitmap b2 = Bitmap.createBitmap(b1, dx1 / 2, dy1 / 2, targetWidth,
                targetHeight);

        if (b1 != source) {
            b1.recycle();
        }

        return b2;
    }

    public static boolean beyondThreshold(long totalMemory) {
        return totalMemory > memoryThreshold;
    }

    public static ExInfo copyExif(String sourcePath, String targetPath) {
        int rotation = 0;
        boolean flip = false;
        try {
            ExifInterface source = new ExifInterface(sourcePath);
            int exifOrientation = source.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            ExifInterface target = new ExifInterface(targetPath);
            target.setAttribute(ExifInterface.TAG_ORIENTATION, String.valueOf(exifOrientation));
            target.saveAttributes();
        } catch (IOException e) {
            System.out.println(e);
            return null;
        }
        return new ExInfo(rotation, flip);
    }

    public static ExInfo defineExifOrientation(String imagePath) {
        int rotation = 0;
        boolean flip = false;
        try {
            ExifInterface exif = new ExifInterface(imagePath);
            int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
            switch (exifOrientation) {
                case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
                    flip = true;
                case ExifInterface.ORIENTATION_NORMAL:
                    rotation = 0;
                    break;
                case ExifInterface.ORIENTATION_TRANSVERSE:
                    flip = true;
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotation = 90;
                    break;
                case ExifInterface.ORIENTATION_FLIP_VERTICAL:
                    flip = true;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotation = 180;
                    break;
                case ExifInterface.ORIENTATION_TRANSPOSE:
                    flip = true;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotation = 270;
                    break;
            }
        } catch (IOException e) {
            System.out.println(e);
            return null;
        }
        return new ExInfo(rotation, flip);
    }

    private static Bitmap cropImage(Bitmap source, int width, int height) {

        Bitmap scaled = ThumbnailUtils.extractThumbnail(source, width, height);
        if (scaled != source) {
            source.recycle();
        }
        return scaled;
    }

    public static class ExInfo {
        private final boolean flip;
        private int rotation;

        public ExInfo(int rotation, boolean flip) {
            this.rotation = rotation;
            this.flip = flip;
        }

        public boolean isFlip() {
            return flip;
        }

        public int getRotation() {
            return rotation;
        }
    }

    public static class RoundedImageProcessor implements BitmapProcessor {
        private final int pixels;

        public RoundedImageProcessor(int dimensionPixelSize) {
            this.pixels = dimensionPixelSize;
        }

        @Override
        public Bitmap process(Bitmap source) {
            Bitmap output = ImageUtils.getRoundedCornerBitmap(source.copy(Bitmap.Config.ARGB_8888, true), pixels);
            return output;
        }

    }

    public static class ThumbnailImageProcessor implements BitmapProcessor {
        private final int width;
        private final int height;

        public ThumbnailImageProcessor(int width, int height) {
            this.width = width;
            this.height = height;
        }

        @Override
        public Bitmap process(Bitmap source) {
            Bitmap output = ImageUtils.cropImage(source, width, height);
            return output;
        }

    }

    public static class RoundedImageTransformation implements Transformation {
        private final int pixels;

        public RoundedImageTransformation(int pixels) {
            this.pixels = pixels;
        }

        @Override
        public Bitmap transform(Bitmap source) {
            Bitmap output = ImageUtils.getRoundedCornerBitmap(source.copy(Bitmap.Config.ARGB_8888, true), pixels);
            source.recycle();
            return output;
        }

        @Override
        public String key() {
            return "[Round the bitmap with " + pixels + "]";
        }
    }


}
